package crane.bomb;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
import java.util.Properties;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;


/**
 * @author crane
 *
 */
public class XMLUtil {
    
    /**
     * DATA path
     */
    public static final String SCORE_DATA_PATH = "data/bomb-score.xml";
    
    /**
     * NUMBER_PER_LEVEL
     */
    public static final int NUMBER_PER_LEVEL = 5;
    
    /*
     * SCORE_DATA
     */
    private static final String SCORE_DATA = "score-data";
    
    /*
     * PLAYER_SCORE_DATA
     */
    private static final String PLAYER_SCORE_DATA = "player-level";
    
    /*
     * key of level id
     */
    private static final String KEY_LEVEL_ID = "id";
    
    /*
     * key of player's level
     */
    private static final String KEY_PLAYER = "player";
    
    /*
     * key of player's Name
     */
    private static final String KEY_PLAYER_NAME = "name";
    
    /*
     * key of player's score
     */
    private static final String KEY_PLAYER_SCORE = "score";
    
    /*
     * Document
     */
    private static Document doc = null;
    
    /**
     * 
     * @param args
     * @throws Exception
     */
    public static final void main(String[] args) throws Exception{
        // readScore(null);
        List<Integer> scoreList = new ArrayList<Integer>();        
        
        int x = 2000;
        for (int i = 0; i < 100; i++) {
            x = x - 90;
            scoreList.add(new Integer(x));
        }
        Collections.sort(scoreList);
        for (int i = 0; i < scoreList.size(); i++) {
            System.out.println(scoreList.get(i));
        }
        
    }

    /**
     * read Score from XML files
     *  
     * @param path
     */
    protected List readXML(String path) {
        doc = readFile(getFilePath(path));
        if (doc == null){
            return null;
        }
        Element root = doc.getDocumentElement();
        NodeList list = root.getElementsByTagName(PLAYER_SCORE_DATA);
        
        String level = null;
        String name = null;
        String score = null;
        String[] scoreArray = null;
        List<String[]> scoreList = new ArrayList<String[]>();        
        
        NodeList node = null;
        Element element = null;        
        Element subElem = null;
        NodeList subList = null;
        for (int i = 0; i < list.getLength(); i++) {
            element = (Element) list.item(i);
            level = element.getAttributeNode(KEY_LEVEL_ID).getValue();
            
            subList = element.getElementsByTagName(KEY_PLAYER);
            
            for (int j = 0; j < subList.getLength(); j++) {                
                subElem = (Element) subList.item(j);
                scoreArray = new String[3];
                
                node = subElem.getElementsByTagName(KEY_PLAYER_NAME);
                name = getNodeValue(node, 0);
                
                node = subElem.getElementsByTagName(KEY_PLAYER_SCORE);
                score = getNodeValue(node, 0);
                
                scoreArray[0] = level;
                scoreArray[1] = name;
                scoreArray[2] = score;
                scoreList.add(scoreArray);
            }
        }
        return scoreList;
    }
    
    /**
     * read Score from XML files
     *  
     * @param path
     */
    protected List<Integer> getLevelScore(String path, int ilevel) {
        doc = readFile(getFilePath(path));
        if (doc == null){
            return null;
        }
        Element root = doc.getDocumentElement();
        NodeList list = root.getElementsByTagName(PLAYER_SCORE_DATA);
        
        String level = null;
        String score = null;
        List<Integer> scoreList = new ArrayList<Integer>();        
        
        NodeList node = null;
        Element element = null;        
        Element subElem = null;
        NodeList subList = null;
        for (int i = 0; i < list.getLength(); i++) {
            element = (Element) list.item(i);
            level = element.getAttributeNode(KEY_LEVEL_ID).getValue();
            if (level == null){
                continue;
            }
            
            if (!level.equals("" + ilevel)){
                continue;
            }
            
            subList = element.getElementsByTagName(KEY_PLAYER);            
            for (int j = 0; j < subList.getLength(); j++) {                
                subElem = (Element) subList.item(j);
                
                node = subElem.getElementsByTagName(KEY_PLAYER_SCORE);
                score = getNodeValue(node, 0);                
                scoreList.add(new Integer(score));
            }
        }
        Collections.sort(scoreList);
        return scoreList;
    }
    
    /**
     * cleanLevelScore
     *  
     * @param path
     * @param ilevel
     */
    protected void cleanLevelScore(Document doc, int ilevel) {

        if (doc == null){
            return;
        }
        Element root = doc.getDocumentElement();
        NodeList list = root.getElementsByTagName(PLAYER_SCORE_DATA);
        
        String level = null;
        Element element = null;        
        Element subElem = null;
        NodeList subList = null;
        for (int i = 0; i < list.getLength(); i++) {
            element = (Element) list.item(i);
            level = element.getAttributeNode(KEY_LEVEL_ID).getValue();
            if (level == null){
                continue;
            }
            
            if (!level.equals("" + ilevel)){
                continue;
            }
            
            subList = element.getElementsByTagName(KEY_PLAYER);
            int number = XMLUtil.NUMBER_PER_LEVEL;
            int len = subList.getLength();
            if (len > number){
                for (int j = number; j < len; j++) {
                    subElem = (Element) subList.item(j);
                    element.removeChild(subElem);
                }
            }
        }
    }

    
    /**
     * write contents into a XML file
     *  
     * @param path
     */
    protected void writeXML(String path, String[] content) {
        
        String filePath = getFilePath(path);
        
        doc = readFile(filePath);
        if (doc == null){
            return;
        }
        
        Element root = doc.getDocumentElement();
        NodeList list = root.getElementsByTagName(PLAYER_SCORE_DATA);
        
        String level = null;
        String score = null;
        NodeList node = null;
        Element element = null;
        Element subElem = null;
        Element preElem = null;
        Element nextElem = null;
        String nextLevel = null;
        NodeList subList = null;

        int[] layer = new int[4];
        int tempLevel = 0;
        int inLevel = Integer.parseInt(content[0]);
        
        outer:
        for (int i = 0; i < list.getLength(); i++) {
            layer[0] = 1;
            element = (Element) list.item(i);
            level = element.getAttributeNode(KEY_LEVEL_ID).getValue();
            
            if (level == null){
                continue;
            }
            tempLevel = Integer.parseInt(level);
            if (inLevel < tempLevel){
                preElem = element;
                break outer; 
            } else if (inLevel == tempLevel){
                layer[1] = 1;
                subList = element.getElementsByTagName(KEY_PLAYER);
                
                for (int j = 0; j < subList.getLength(); j++) {
                    layer[2] = 1;
                    subElem = (Element) subList.item(j);
                    node = subElem.getElementsByTagName(KEY_PLAYER_SCORE);
                    score = getNodeValue(node, 0);
                    
                    if (score != null && Integer.parseInt(content[2]) < Integer.parseInt(score)){
                        layer[3] = 1;
                        break outer;
                    }
                }
                break outer;
            }else{
                // The last node
                if (i + 1 == list.getLength()){
                    break outer;
                }

                nextElem = (Element) list.item(i + 1);
                nextLevel = nextElem.getAttributeNode(KEY_LEVEL_ID).getValue();
                if (nextLevel != null && inLevel < Integer.parseInt(nextLevel)){
                    preElem = nextElem;
                    break outer;
                }
            }
        }
        
        Element newElem = null;
        if (layer[3] == 1){
            newElem = createScoreElement(3, content);
            element.insertBefore(newElem, subElem);            
        } else if (layer[2] == 1 || layer[1] == 1){
            newElem = createScoreElement(3, content);
            element.appendChild(newElem);
        } else if (layer[0] == 1){
            newElem = createScoreElement(2, content);
            if (preElem != null){
                root.insertBefore(newElem, preElem);
            } else {
                root.appendChild(newElem);
            }
        } else {
            newElem = createScoreElement(1, content);
            root.appendChild(newElem);           
        }
        
        cleanLevelScore(doc, inLevel);
        
        // updateXml(doc, filePath);
        XMLUtil xmlUtil = new XMLUtil();
        xmlUtil.saveXml(doc, filePath);

    }
    
    
    /**
     * Get Node's Value
     * 
     * @param node
     * @param idx
     * @return String
     */
    private String getNodeValue(NodeList node, int idx){
        if (node.getLength() > idx){
            Element element = (Element) node.item(idx);
            return element.getFirstChild().getNodeValue();
        } else {
            return null;
        }        
    }
    
    
    /**
     * getResourceAsStream
     * @param filePath
     * @return stream
     */
    private InputStream getResourceAsStream(String filePath) {
        InputStream stream = null;
        try {
            stream = new FileInputStream(filePath);
        } catch (Exception exception) {

        }
        return stream;
    }
    
    /**
     * Create Element
     * 
     * @param layer (1-3 layer)
     * @param newContents
     */
    private Element createScoreElement(int layer, String[] newContents) {
        
        Element element = null;
        Element subNode = null;
        // make a node
        if (layer < 2){          
            
            element = doc.createElement(SCORE_DATA);
            Element subElem = doc.createElement(PLAYER_SCORE_DATA);
            subElem.setAttribute(KEY_LEVEL_ID, newContents[0]);

            subNode = doc.createElement(KEY_PLAYER);            
            subNode = createSubElement(subNode, newContents);
            
            subElem.appendChild(subNode);
            element.appendChild(subElem);
            
        } else if (layer == 2){
            element = doc.createElement(PLAYER_SCORE_DATA);
            element.setAttribute(KEY_LEVEL_ID, newContents[0]);
            
            subNode = doc.createElement(KEY_PLAYER);
            subNode = createSubElement(subNode, newContents);
            element.appendChild(subNode);            
        } else {
            element = doc.createElement(KEY_PLAYER);
            element = createSubElement(element, newContents);
        }
        return element;
    }
    
    /**
     * Create Element
     * 
     * @param newContents
     */
    private Element createSubElement(Element element, String[] newContents) {
        
        Element newElem = element;
        
        //    make name
        Element subNode = doc.createElement(KEY_PLAYER_NAME);
        subNode.appendChild(doc.createTextNode(newContents[1]));
        newElem.appendChild(subNode);

        //    make score
        subNode = doc.createElement(KEY_PLAYER_SCORE);
        subNode.appendChild(doc.createTextNode(newContents[2]));
        newElem.appendChild(subNode);
        return newElem;
    }
    

    /**
     * Save XML file
     *
     *@param doc Document
     *@param filePath
     *@deprecated
     */
    private boolean updateXml(Document doc, String filePath) {
        
        try {
            TransformerFactory tf = TransformerFactory.newInstance();
            Transformer transformer = tf.newTransformer();
            DOMSource source = new DOMSource(doc);

            PrintWriter pw = new PrintWriter(new FileOutputStream(filePath));
            StreamResult result = new StreamResult(pw);
            
            Properties pformat = transformer.getOutputProperties();
            pformat.setProperty(OutputKeys.ENCODING, "GB2312");
            pformat.setProperty(OutputKeys.INDENT, "yes");
            transformer.setOutputProperties(pformat);
            transformer.transform(source, result);

        } catch (TransformerException mye) {
            mye.printStackTrace();
            return false;
        } catch (IOException exp) {
            exp.printStackTrace();
            return false;
        }
        return true;
    }
    
    /**
     * Save xml file
     * 
     * @param doc
     * @param filePath
     * @return boolean
     */
    private boolean saveXml(Document doc, String filePath){
        FileWriter fout = null;
        try {
            OutputFormat format = new OutputFormat(doc);
            format.setLineSeparator("\n");
            format.setIndenting(true);
            format.setEncoding("GB2312");
            fout = new FileWriter(filePath);
            XMLSerializer serial = new XMLSerializer(fout, format);
            serial.asDOMSerializer();
            serial.serialize(doc.getDocumentElement());
            fout.close();
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
    
    
    /**
     * Read file
     * 
     * @param path
     * @return Document
     */
    private Document readFile(String path){

        DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = null;
        try {
            builder = dbfactory.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        }

        InputStream is = getResourceAsStream(path);

        try {
            doc = builder.parse(is);
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return doc;
    }
    
    /**
     * get file's path
     * 
     * @param path
     * @return String
     */
    private String getFilePath(String path){
        if (path == null || path.trim() == ""){
            path = SCORE_DATA_PATH;
        }
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        URL url = loader.getResource(path);
        return url.getPath();
    }


}
